之前po過部分swift語法的筆記,以下再附上近期整理好的部分
class沒有成員逐一建構器這個功能,而struct有。
class GameCharacter {
var attackSpeed = 1.0
let gender : ""
var name: String? // name為一個可選型別String?,會被自動指派為一個預設值nil,表示一開始沒有name值。
// 這樣可以不用在建構時給它值
init(gender : String) { //建構子
gender = "man" //建構時指派值
}
}
繼承:
class Boss : GameCharacter { // Boss繼承自GameCharacter
var name = String? = nil
init(){
super.init(gender : "girl")
}
}
// 定義一個結構 有兩個建構器
struct Color {
let red, green, blue: Double
// 這個建構器有寫外部參數名稱跟內部參數名稱
init(red r: Double, green g : Double, blue b: Double) {
self.red = r
self.green = g
self.blue = b
}
// 這個建構器則是合併成一個參數名稱 外部跟內部參數名稱相同
init(white: Double) {
red = white
green = white
blue = white
}
}
var oneColor = Color(red: 0.9, green: 0.5, blue: 0.5)
var anotherColor = Color(white: 1.0)
與函式跟方法一樣,可以省略其外部參數名稱,使用 下底線 _
替代外部參數名稱,如下:
struct SomeNumbers {
let number: Int
init( _ n: Int) { // 使用下底線 _ 表示要省略外部參數名稱
number = n
}
}
// 生成一個實體時 參數前就不需要有外部參數名稱
var oneNumbers = SomeNumbers(9)
使用時機:
需要封裝的資料量較少且較簡單。
在指派或傳遞這個實體時,有特別需求這個資料是會被拷貝而不是參考。
不需要去繼承另一個已存在型別的屬性或行為。
所有struct都有一個自動生成的成員逐一建構器(memberwise initializer
),當要生成一個結構的實體時,用來初始化實體內的屬性,如下:
struct CharacterStats {
var hp = 0.0
var mp = 0.0
}
var oneStats = CharacterStats(hp: 120, mp: 100) // 建構出實體
var anotherStats = oneStats
// 這時修改 anotherStats 的 hp 屬性
anotherStats.hp = 300
print(anotherStats.hp) // 可以看出來已經改變了 印出:300.0
// 但 oneStats 的屬性不會改變
// 仍然是被生成實體時的初始值 印出:120.0
print(oneStats.hp)
被儲存於常數的結構不能改變其內變數的值;而被儲存於常數的類別可以改變其內變數的值
// 生成一個 CharacterStats 結構的實體 並指派給一個常數 someStats
let someStats = CharacterStats(hpValueMax: 900, mpValueMax: 150)
someStats.hpValue = 1200 // 這行會報錯誤。這個實體 someStats 為一個常數 所以即使 hpValue 為一個變數屬性 仍然不能修改hpValue的值
關鍵字 lazy
只能使用在變數,因為屬性的值在實體建構完成之前可能無法得到,而常數屬性在建構完成之前必須要有初始值。
使用延遲儲存屬性可以讓類別中如果需要大量計算才能初始化的屬性,在需要的時候才真的初始化它
class DataImporter { // 這個類別會導入外部檔案並執行一些操作 初始化可能會花費不少時間
var fileName = "data.txt" // 這邊簡化成一個檔案名稱 實際上可能會有很多操作
}
class DataManager { // 接著定義另一個類別 DataManager
// 延遲儲存屬性
lazy var importer = DataImporter()
// 操作時需要用到的資料
var data = [String]()
// 簡化內部內容 可能還有許多操作資料的動作
}
let manager = DataManager() // 生成一個類別 DataManager 的實體常數
// 添加一些資料
manager.data.append("Some data")
manager.data.append("Some more data")
// 到目前為止 manager 的 importer 都尚未被初始化
// 直到第一次使用這個屬性 才會被創建並初始化
print(manager.importer.fileName)
self 關鍵字類似 Java 的 this 關鍵字
覆寫屬性時,需要使用getter
(以及有時可省略的setter
)來覆寫繼承來的屬性,且一定要寫上屬性的名稱及型別,這樣才能確定是從哪一個屬性繼承而來的。
可以將一個繼承來的唯讀屬性覆寫為一個讀寫屬性,但不行將一個讀寫屬性覆寫為唯獨屬性。即原本有setter
的話,覆寫時就一定要有setter
。
class AnotherHunter: Archer {
// 覆寫父類別的屬性 重新實作 getter 跟 setter
override var attackSpeed: Double {
get {
return 2.4
}
set {
print(newValue)
}
}
// 省略其他內容
}
Swift 提供了 do-catch 機制來攔截程式執行時發生的錯誤。
do{
//會有例外丟出的函數呼叫放這
let fm = FileManager.default
try fm.removeItem(atPath:"檔名") //這邊要加 try
} catch {
//抓到錯誤時的處理
print(error.localizedDescription) //印出錯誤訊息
}
如果想 自訂錯誤的類型,必須用 enum
定義。以下為遵從 Error protocol 的型別 GoAfterGirlError
,用它來表達追求女生失敗的各種原因:
enum GoAfterGirlError:Error {
case poorProblem
case tooYoungProblem
case notAquariusProblem
}
當程式判斷錯誤發生時,必須用關鍵字 throw
丟出錯誤。而且唯有乖乖遵從 Error protocol 的型別,才能被當成錯誤丟出。
而當 function 裡的程式碼有可能丟出錯誤時,這個 function 的定義還必須加上 throws
,加在( ) 後,警告大家它很危險,有可能丟出錯誤 :
func goAfterAngelababy(money:Int, age:Int) throws {
guard money > 10000 else {
throw GoAfterGirlError.poorProblem
}
guard age > 18 else {
throw GoAfterGirlError.tooYoungProblem
}
print("我追到 Angelababy 了!")
}
//當我們呼叫的 function 有可能丟出錯誤,也就是它有加上 throws 時,我們還要補上 try 才能呼叫。
try goAfterAngelababy(money: 1000, age: 30)
Swift 強制要求我們錯誤一定要處理,不處理將產生 compile error 提醒我們。
如果函式有回傳值,而且可能返回 nil 的話,可以使用 try? 來呼叫函式,避免 APP 當掉
let ans = try? divided(10, by:0) //divided 函式如果分母為0時,會拋出錯誤,所以這邊會回傳nil